Celem raportu jest sprawdzenie i porównanie jakości działania różnych metod klasteryzacji na trzech autorskich zbiorach (dwa z nich wymiaru $R^2$ i jeden $R^3$. Zbiory zostały wytworzone w programie python i zapisane w postaci plików .data oraz .labels0. Następnie za pomocą funkcji zdefiniowanych w pliku Padpy_generowanie_csv.py wyniki benchmarków zostały zapisane w pliku .csv, z którego będziemy korzystać tworząc ten raport. Za pomocą wyżej wspomnianych funkcji wygenerowane zostały również wykresy jako pliki .png, które będą zawarte w poniższym raporcie.
Do porównania jakości klasteryzacji wykorzystane zostały następujące metody:
algorytmy hierarchiczne - z wykorzystaniem funkcji AgglomerativeClustering z następującymi parametrami:
algorytm DBSCAN (Density-Based Spatial Clustering of Applications with Noise) - klasteryzacja dla najbardziej zagęszczonych punktów
Porównanie zostanie również działanie zaimplementowanego algorytmu spektralnego dla różnych parametrów M, równych: 1%, 5%, 10% lub 30% wszystkich obserwacji w danym zbiorze. W przypadku, gdy dla żadnej z tych wartości parametru metoda nie działała dobrze, została również sprawdzona metoda z ręcznie wprowadzoną wartością M, niezależnie od wielkości zbioru.
Jakość wyników poszczególnych algorytmów zostanie porównana za pomocą:
indeksu Fowlkesa–Mallowsa (FMI) - zwraca podobieństwo między zwracanymi klastrami, obliczany za pomocą wzoru: $$\sqrt{\frac{TP}{TP+FP} \cdot \frac{TP}{TP+FN}}$$
skorygowanego indeksu Randa (ARI) - jest to indeks Randa skorygowany o prawdopodobieństwo przydziału punktów do danego klastra, gdzie indeks Randa jest wyrożony nastepującą furmułą: $$\sqrt{\frac{TP+TN}{TP+FP+FN+TN}}$$
gdzie:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import Image
import plotly.graph_objects as go
def wykres1(index_ARI, index_FMI, names, plot_title):
"""
Funkcja przyjmuje argumenty:
index_ARI - type: list of floats, lista wspolczynnikow ARI
index_FMI - type: list of floats, lista wspolczynnikow FMI
names - type: list of strings, lista z nazwami metod uzytych do porownania
plot_title - type: string, tytul wykresu
funkcja rysuje wykres slupkowy interaktywny za pomoca pakietu Plotly
"""
fig = go.Figure(data=[
go.Bar(name='Fowlkes-Mallows index', x=names, y=np.round(index_FMI,4)),
go.Bar(name='Adjusted Rand index', x=names, y=np.round(index_ARI,4))],
)
hovertemplate = "<b>%{x}</b><br>" +\
"Index value: %{y}<br>"
fig.update_traces(
hovertemplate=hovertemplate
)
fig.update_layout(barmode="group",
title= {
'text' : '<b>'+ plot_title +'</b>',
'x':0.5,
'xanchor': 'center'})
fig.show()
return
plots_title = "Wykres wartości indeksów FMI i ARI dla poszczególnych algorytmów klasteryzacji"
def tabelka(names, index_ARI, index_FMI, w=1050, h=350):
"""
Funkcja przyjmuje argumenty:
index_ARI - type: list of floats, lista wspolczynnikow ARI
index_FMI - type: list of floats, lista wspolczynnikow FMI
names - type: list of strings, lista z nazwami metod uzytych do porownania
"""
fig = go.Figure(data=[go.Table(header=dict(values=['Method', 'Fowlkes-Mallows index',
'Adjusted Rand index']),
cells=dict(values=[names, np.round(index_FMI,4), np.round(index_ARI,4)]))
])
fig.update_layout(width=w, height=h)
fig.show()
return
def tabelka_std(names, index_ARI, index_FMI,index_ARI_std,index_FMI_std, w=1000, h=350):
"""
Funkcja przyjmuje argumenty:
index_ARI - type: list of floats, lista wspolczynnikow ARI
index_FMI - type: list of floats, lista wspolczynnikow FMI
names - type: list of strings, lista z nazwami metod uzytych do porownania
index_ARI_std - type: list of floats, lista wspolczynnikow ARI dla zmiennych zestandaryzowanych
index_FMI_std - type: list of floats, lista wspolczynnikow FMI dla zmiennych zestandaryzowanych
"""
fig = go.Figure(data=[go.Table(header=dict(values=['Method', 'FMI', 'ARI',
'FMI for standarized values', 'ARI for standarized values']),
cells=dict(values=[names, np.round(index_FMI,4), np.round(index_ARI,4),
np.round(index_FMI_std,4), np.round(index_ARI_std,4)]))
])
fig.update_layout(width=w, height=h)
fig.show()
return
Zdefiniujmy ścieżkę do wczytywanych plików oraz funkcję która będzie wczytywała pliki .csv i podawała informacje o wymiarach zbioru i liczbie klastrów.
path1 = r"C:\Users\patry\OneDrive\Pulpit\Zdalne pw\Python - PadPy\Projekt_23_dane_benchmarkowe"
def wczytaj(path, file_name):
zbior = pd.read_csv(path + '\\' + file_name, sep=';')
print("Wymiary zbioru: ", zbior["Shape"][0])
print("Liczba klastrów", zbior["Number of clusters"][0])
return zbior
zbior1 = wczytaj(path1, 'zbior1_4.csv')
Widzimy, że zbior1 jest dwuwymiarowy i ma 4 odgórnie zdefiniowane klastry. Zbiór został stworzony za pomocą pakietu numpy oraz zdefiniowanych funkcji X(t) oraz Y(t), których wartości obrazują okręgi oraz kształt 'odwróconego serca'. Wykres, wraz z podziałem punktów na klastry zawarte w pliku .labels0, wygląda następująco:
Image(path1 + '\\' + "zbior1_4_main.png", width=500, height=500)
wykres1(list(zbior1['Adjusted Rand index']), list(zbior1['Fowlkes-Mallows index']), list(zbior1['Method']), plots_title)
Na podstawie wykresu możemy zauważyć, że metody:
są równoważne z podziałem zawartym w pliku .labels0. Pozostałe metody działają już zdecydowanie gorzej. Zobaczmy to na wykresach:
Image(path1 + '\\' + "zbior1_4_builtin_methods.png", width=1000, height=500)
Image(path1 + '\\' + "zbior1_4_spectral_algorithm.png", width=1000, height=500)
Istotnie na wykresach widać, że wspomniane metody z dwoma współczynnikami o wartościach równych 1 dzielą zbiory zdecydowanie najlepiej.
zbior2 = wczytaj(path1, 'zbior2_2.csv')
Image(path1 + '\\' + "zbior2_2_main.png", width=500, height=500)
zbior2 jest dwuwymiarowy i ma 2 odgórnie zdefiniowane klastry. Powstał za pomocą wygenerowania 450 punktów z rozkładu jednostajnego ciągłego na przedziale [-6,0] oraz 300 punktów na przedziale [-3,3]. Następnie na wygenerowanych punktach zdefiniowano funkcje, odpowiednio:
wykres1(list(zbior2['Adjusted Rand index']), list(zbior2['Fowlkes-Mallows index']), list(zbior2['Method']), plots_title)
Na podstawie wartości indeksów, szczególnie indeksu ARI widzimy, że zbiór został dobrze podzielony jedynie po użyciu metod: Genie, hierarchicznej z użyciem parametru single linkage oraz algorytmu spektralnego z parametrami M równymi 5% i 10% liczby obserwacji w zbiorze (czyli odpowiedno M=37 i M=75). Zobaczmy to na wykresach obrazujących podział punktów na klastry przez poszczególne metody:
Image(path1 + '\\' + "zbior2_2_builtin_methods.png", width=1000, height=500)
Image(path1 + '\\' + "zbior2_2_spectral_algorithm.png", width=1000, height=500)
W tym przypadku również widać zdecydowanie lepsze działanie metod ze współczynnikami FMI oraz ARI równymi 1 od pozostałych.
zbior3 = wczytaj(path1, 'zbior3_3.csv')
Image(path1 + '\\' + "zbior3_3_main.png", width=500, height=500)
zbior3 ma 3 wymiary i 3 odgórnie zdefiniowane klastry. Powstał za pomocą wygenerowania:
wykres1(list(zbior3['Adjusted Rand index']), list(zbior3['Fowlkes-Mallows index']), list(zbior3['Method']), plots_title)
Dla tego zbioru jedynie algorytmy: Genie oraz spektralny dla M=1% obserwacji działają poprawnie. Dla pozostałych metod oba współczynniki są zdecydowanie niższe. Różnica w jakości działania algorytmów jest zauważalna na poniższych wykresach:
Image(path1 + '\\' + "zbior3_3_bulitin_methods.png", width=1000, height=500)
Image(path1 + '\\' + "zbior3_3_spectral_algorithm.png", width=1000, height=500)
Zbadamy teraz wpływ standaryzacji zmiennych na jakość klasteryzacji różnych metod. Użyto do tego funkcji StandardScaler z pakietu sklearn.
zbior1_std = wczytaj(path1, 'zbior1_std_4.csv')
Poniżej zostały przedstawione wykresy obrazujące wartości indeksów przed i po standaryzacji zmiennych.
wykres1(list(zbior1['Adjusted Rand index']), list(zbior1['Fowlkes-Mallows index']), list(zbior1['Method']), plots_title)
wykres1(list(zbior1_std['Adjusted Rand index']), list(zbior1_std['Fowlkes-Mallows index']), list(zbior1_std['Method']),
plots_title + "<br> po standaryzacji zmiennych </br>")
Dla zbioru pierwszego widzimy małą różnicę w wartościach współczynników przed i po standaryzacji zmiennych. Zauważalny jest jedynie znaczny spadek wartości indeksu ARI dla metody DBSCAN.
zbior2_std = wczytaj(path1, 'zbior2_std_2.csv')
Poniżej zostały przedstawione tabele obrazujące wartości indeksów przed i po standaryzacji zmiennych.
tabelka_std(list(zbior2['Method']), list(zbior2['Adjusted Rand index']), list(zbior2['Fowlkes-Mallows index']),
list(zbior2_std['Adjusted Rand index']),list(zbior2_std['Fowlkes-Mallows index']), w=1000, h=650)
Obserwując wartości w tabelach ponownie widzimy znaczne pogorszenie jakości metody DBSCAN. Za to po standaryzacji zmiennych, bardzo wysokie wartości obu współczynników zanotowała metoda k-średnich.
zbior3_std = wczytaj(path1, 'zbior3_std_3.csv')
wykres1(list(zbior3['Adjusted Rand index']), list(zbior3['Fowlkes-Mallows index']), list(zbior3['Method']), plots_title)
wykres1(list(zbior3_std['Adjusted Rand index']), list(zbior3_std['Fowlkes-Mallows index']), list(zbior3_std['Method']),
plots_title + "<br> po standaryzacji zmiennych </br>")
Dla trzeciego zbioru po standaryzacji zmiennych różnicę widzimy jedynie w przypadku metody spektralnej - dla M=1% obserwacji wartości współczynników uległy pogorszeniu.
Na podstawie powyżej omówionych zbiorów możemy wysunąć następujące wnioski: